package com.hanter.android.radwidget.cupertino;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;

import com.etoonet.android.radwidget.R;
import com.hanter.android.radwidget.blur.BlurView;
import com.hanter.android.radwidget.blur.RenderScriptBlur;

import java.util.ArrayList;
import java.util.List;

@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class CupertinoTabBar extends FrameLayout implements TabBar, View.OnFocusChangeListener {

    private static final String TAG = "CupertinoTabBar";

    private Paint borderPaint;
    private int iconSize;
    private int activeColor;
    private int inactiveColor;
    private int backgroundColor;

    private BlurView blurView;
    private LinearLayout tabBarContainer;

    private int currentTab = -1;
    private List<TabBarItem> tabItems = new ArrayList<>(2);

    @Nullable
    private OnTabChangeListener onTabChangeListener;
    @Nullable
    private OnTabClickListener onTabClickListener;

    public CupertinoTabBar(Context context) {
        this(context, null);
    }

    public CupertinoTabBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CupertinoTabBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setChildrenDrawingOrderEnabled(true);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CupertinoTabBar, defStyleAttr, 0);

        activeColor = a.getColor(R.styleable.CupertinoTabBar_ctb_activeColor, CupertinoColors.activeBlue);
        inactiveColor = a.getColor(R.styleable.CupertinoTabBar_ctb_inactiveColor, CupertinoColors.inactiveGray);
        iconSize = a.getDimensionPixelSize(R.styleable.CupertinoTabBar_ctb_iconSize,
                context.getResources().getDimensionPixelSize(R.dimen.cupertino_tabbar_icon_size));
        int borderColor = a.getColor(R.styleable.CupertinoTabBar_ctb_borderColor, Color.TRANSPARENT);
        backgroundColor = a.getColor(R.styleable.CupertinoTabBar_ctb_backgroundColor, 0xCCF8F8F8);
        a.recycle();

        borderPaint = new Paint();
        borderPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
        borderPaint.setAntiAlias(true);
        borderPaint.setColor(borderColor);

        View.inflate(context, R.layout.view_cupertino_tabbar, this);
        blurView = findViewById(R.id.blurView);
        tabBarContainer = findViewById(R.id.tabbarContainer);
        blurView.setOverlayColor(backgroundColor);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public CupertinoTabBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        setChildrenDrawingOrderEnabled(true);
        init(context, attrs, defStyleAttr);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (borderPaint.getColor() != Color.TRANSPARENT)
            canvas.drawLine(0, 0, blurView.getMeasuredWidth(), 0, borderPaint);
    }

    /**
     * Returns the tab indicator view at the given index.
     *
     * @param index the zero-based index of the tab indicator view to return
     * @return the tab indicator view at the given index
     */
    public View getChildTabViewAt(int index) {
        return tabBarContainer.getChildAt(index);
    }

    /**
     * Returns the number of tab indicator views.
     *
     * @return the number of tab indicator views
     */
    public int getTabCount() {
        return tabBarContainer.getChildCount();
    }

    @Override
    public void childDrawableStateChanged(View child) {
        if (getTabCount() > 0 && child == getChildTabViewAt(currentTab)) {
            // To make sure that the bottom strip is redrawn
            invalidate();
        }
        super.childDrawableStateChanged(child);
    }

    @Override
    public void setCurrentTab(int index) {
        if (index < 0 || index >= getTabCount() || index == currentTab) {
            return;
        }

        final int oldTab = currentTab;

        if (oldTab != -1) {
            getChildTabViewAt(oldTab).setSelected(false);
        }

        getChildTabViewAt(index).setSelected(true);
        getChildTabViewAt(index).requestFocus();

        this.currentTab = index;

        onTabChanged(getTabBarItem(this.currentTab));
    }

    protected void onTabChanged(@Nullable TabBarItem item) {
        Log.d(TAG, "onTabChanged");

        if (onTabChangeListener != null) {
            onTabChangeListener.onTabChanged(item);
        }
    }

    @Override
    public int getCurrentTab() {
        return this.currentTab;
    }

    public TabBarItem getTabBarItem(int position) {
        return this.tabItems.get(position);
    }

    @Override
    public void setCurrentTabByTag(String tag) {
        for (int i = 0, count = tabItems.size(); i < count; i++) {
            if (tabItems.get(i).getTag().equals(tag)) {
                setCurrentTab(i);
                break;
            }
        }
    }

    @Override
    public String getCurrentTabTag() {
        if (currentTab >= 0 && currentTab < tabItems.size()) {
            return tabItems.get(currentTab).getTag();
        }
        return null;
    }

    @Override
    public CharSequence getAccessibilityClassName() {
        return CupertinoTabBar.class.getName();
    }

    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);

        final int count = getTabCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildTabViewAt(i);
            child.setEnabled(enabled);
        }
    }

    public List<TabBarItem> getTabBarItems() {
        return tabItems;
    }

    public void setTabBarItems(List<TabBarItem> items) {
        this.tabItems.clear();
        this.tabItems.addAll(items);

        removeAllTabViews();

        for (TabBarItem tabBarItem : items) {
            addTab(tabBarItem);
        }

        setCurrentTab(0);
    }

    public void addTab(int itemId, int icon, String text) {
        TabBarItem item = new TabBarItem(itemId, icon, text, null);
        addTab(item);
    }

    public void addTab(int icon, String text, @NonNull String tag) {
        TabBarItem item = new TabBarItem(icon, text, tag);
        addTab(item);
    }

    public void addTab(TabBarItem item) {
        addTabItemView(item);

        if (currentTab == -1) {
            setCurrentTab(0);
        }
    }

    private View createTabBarItemView(TabBarItem item) {
        return ItemHolder.create(this, item).itemView;
    }

    private void addTabItemView(TabBarItem item) {
        this.tabItems.add(item);

        View tabView = createTabBarItemView(item);

        if (tabView.getLayoutParams() == null) {
            final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                    0, ViewGroup.LayoutParams.MATCH_PARENT, 1.0f);
            lp.setMargins(0, 0, 0, 0);
            tabView.setLayoutParams(lp);
        }

        // Ensure you can navigate to the tab with the keyboard, and you can touch it
        tabView.setFocusable(true);
        tabView.setClickable(true);

        tabBarContainer.addView(tabView);

        tabView.setOnClickListener(new TabClickListener(CupertinoTabBar.this,
                tabBarContainer.getChildCount() - 1));
    }

    private void removeAllTabViews() {
        tabBarContainer.removeAllViews();
        this.tabItems.clear();
        currentTab = -1;
    }

    @Override
    public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
        if (!isEnabled()) {
            return null;
        }
        return super.onResolvePointerIcon(event, pointerIndex);
    }

    public void setupBlur(ViewGroup rootView) {
        Drawable background = ContextCompat.getDrawable(getContext(), R.color.tabbarBackgroundColor);
        blurView.setupWith(rootView)
                .setFrameClearDrawable(background)
                .setBlurAlgorithm(new RenderScriptBlur(getContext()))
                .setBlurEnabled(!isTabBarOpaque())
                .setBlurRadius(10)
                .setHasFixedTransformationMatrix(true);
    }

    @Nullable
    public OnTabChangeListener getOnTabChangeListener() {
        return onTabChangeListener;
    }

    public void setOnTabChangeListener(@Nullable OnTabChangeListener onTabChangeListener) {
        this.onTabChangeListener = onTabChangeListener;
    }

    @Nullable
    public OnTabClickListener getOnTabClickListener() {
        return onTabClickListener;
    }

    public void setOnTabClickListener(@Nullable OnTabClickListener onTabClickListener) {
        this.onTabClickListener = onTabClickListener;
    }

    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        // No-op. Tab selection is separate from keyboard focus.
    }

    private boolean isTabBarOpaque() {
        return Color.alpha(backgroundColor) == 0xFF;
    }

    public void setTabBarBackgroundColor(@ColorInt int color) {
        this.backgroundColor = color;
        this.tabBarContainer.setBackgroundColor(color);
        this.blurView.setBlurEnabled(isTabBarOpaque());
    }

    static class ItemHolder {

        View itemView;
        ImageView icon;
        TextView title;

        ItemHolder(CupertinoTabBar tabBar, TabBarItem item) {
            itemView = LayoutInflater.from(tabBar.getContext())
                    .inflate(R.layout.item_cupertino_tabbar, tabBar.tabBarContainer, false);
            icon = itemView.findViewById(R.id.icon);

            ViewGroup.LayoutParams layoutParams = icon.getLayoutParams();
            layoutParams.width = tabBar.iconSize;
            layoutParams.height = tabBar.iconSize;
            icon.setLayoutParams(layoutParams);

            icon.setImageResource(item.getIcon());
            title = itemView.findViewById(R.id.title);
            title.setText(item.getTitle());

            int[][] states = new int[][]{
                    new int[]{android.R.attr.state_selected},  // pressed
                    new int[]{} // enabled
            };

            int[] colors = new int[]{
                    tabBar.activeColor,
                    tabBar.inactiveColor
            };

            ColorStateList colorStateList = new ColorStateList(states, colors);
            title.setTextColor(colorStateList);
        }

        static ItemHolder create(CupertinoTabBar cupertinoTabBar, TabBarItem item) {
            return new ItemHolder(cupertinoTabBar, item);
        }
    }

    // registered with each tab indicator so we can notify tab host
    private static class TabClickListener implements OnClickListener {
        private final CupertinoTabBar tabBar;
        private final int tabIndex;

        private TabClickListener(CupertinoTabBar tabBar, int tabIndex) {
            this.tabBar = tabBar;
            this.tabIndex = tabIndex;
        }

        public void onClick(View v) {
            if (tabBar.onTabClickListener != null) {
                tabBar.onTabClickListener.onTabClick(v, tabIndex);
            }

            if (tabBar.currentTab != tabIndex) {
                tabBar.setCurrentTab(tabIndex);

                if (tabBar.onTabChangeListener != null) {
                    tabBar.onTabChangeListener.onTabChanged(tabBar.tabItems.get(tabIndex));
                }
            }
        }
    }

    /**
     * Lets TabBar know that the user clicked on a tab indicator.
     */
    public interface OnTabChangeListener {
        void onTabChanged(TabBarItem item);
    }

    public interface OnTabClickListener {
        void onTabClick(View view, int index);
    }

}
